package com.pandora.credit.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.pandora.common.util.*;
import com.pandora.credit.dao.CustomerDao;
import com.pandora.credit.dto.request.CertificationRequest;
import com.pandora.credit.dto.request.CustomerLoansRequest;
import com.pandora.credit.dto.request.LoginRequest;
import com.pandora.credit.dto.request.ResetPasswordRequest;
import com.pandora.credit.dto.response.AuthorizationTokenResponse;
import com.pandora.credit.dto.response.LoanResponseNextStr;
import com.pandora.credit.entity.Customer;
import com.pandora.credit.entity.Terminal;
import com.pandora.credit.exception.auth.AuthorizationTokenInvalidException;
import com.pandora.credit.exception.EmptyRequestException;
import com.pandora.credit.exception.InvalidResponseException;
import com.pandora.credit.exception.password.MobilePasswordRequiredException;
import com.pandora.credit.exception.auth.AccountAuthFailureException;
import com.pandora.credit.exception.certification.Card4EmptyException;
import com.pandora.credit.exception.certification.CertificationFrequentlyException;
import com.pandora.credit.exception.certification.CertificationInvalidException;
import com.pandora.credit.exception.customer.CustomerNotExistsException;
import com.pandora.credit.exception.customer.CustomerNotFoundException;
import com.pandora.credit.exception.password.ParameterSimplePwdErrorException;
import com.pandora.credit.service.CommonService;
import com.pandora.credit.service.CustomerService;
import com.pandora.credit.service.TerminalService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.sql.Timestamp;
import java.util.Calendar;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @author developer
 */
@Service
public class CustomerServiceImpl implements CustomerService {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private CustomerDao customerDao;
    @Autowired
    private TerminalService terminalService;
    @Autowired
    private CommonService commonService;

    @Override
    public Customer getByMobile(String mobile) {
        return customerDao.queryByMobile(mobile);
    }

    @Override
    public void save(Customer customer) {
        customerDao.insert(customer);
    }

    @Override
    public void update(Customer customer) {
        customerDao.update(customer);
    }

    @Override
    public List<Customer> getByStatusArea(String status) {
        return customerDao.queryByStatusArea(status);
    }

    @Override
    public AuthorizationTokenResponse resetPassword(ResetPasswordRequest req, String authorizationToken) {
        if (req == null) {
            throw new EmptyRequestException();
        }
        String password = req.getPassword();
        if (!RegexUtil.isSimplePass(password)) {
            throw new ParameterSimplePwdErrorException();
        }

        String mobile = JedisUtil.get(Constants.BUSINESS_REDIS_URL,
                StringUtils.getStrParam(Constants.AUTHORIZATION_TOKEN_KEY, authorizationToken));
        if (VerifyUtil.isEmpty(mobile)) {
            throw new AuthorizationTokenInvalidException();
        }

        Customer acu = getByMobile(mobile);
        if (acu == null) {
            throw new CustomerNotFoundException();
        }
        logger.info("Change password for mobile: {}", mobile);

        String pwdFromPython = HttpUtil.sendPostPassword(password);
        if (VerifyUtil.isEmpty(pwdFromPython)){
            throw new ParameterSimplePwdErrorException();
        }

        acu.setPasswordDigest(pwdFromPython);
        if (acu.getStatus() != Constants.CUSTOMER_STATUS_BOUND) {
            acu.setStatus(Constants.CUSTOMER_STATUS_REGISTERED);
        }
        acu.setUpdateTime(DateUtil.getNowTimestamp());
        update(acu);
        JedisUtil.set(Constants.BUSINESS_REDIS_URL,
                StringUtils.getStrParam(Constants.MOBILE_AUTHORIZATION_TOKEN_KEY, mobile),
                authorizationToken,
                Constants.REGISTER_TOKEN_EXPIRATION);
        JedisUtil.set(Constants.BUSINESS_REDIS_URL,
                StringUtils.getStrParam(Constants.AUTHORIZATION_TOKEN_KEY, authorizationToken),
                mobile,
                Constants.REGISTER_TOKEN_EXPIRATION);
        return new AuthorizationTokenResponse(acu, authorizationToken);
    }

    @Override
    public AuthorizationTokenResponse requestCertification(CertificationRequest certificationRequest, String authorizationToken) {
        if (certificationRequest == null) {
            throw new EmptyRequestException();
        }
        String name = certificationRequest.getName();
        String idCardNo = certificationRequest.getIdCardNo();
        String bankCardNo = certificationRequest.getBankCardNo();
        String mobile = certificationRequest.getMobile();
        logger.info("{user_name: {}}, {id_card_no: {}}, {bank_card_no: {}}, {mobile: {}}", name, idCardNo, bankCardNo, mobile);
        if (VerifyUtil.isEmpty(authorizationToken)) {
            throw new AuthorizationTokenInvalidException();
        }
        int leftTimes = checkCertificationRate(mobile);
        if (leftTimes < 0) {
            throw new CertificationFrequentlyException(StringUtils.getStrParam("今天%s次认证机会已用完，请您明天再试！",
                    String.valueOf(Constants.CERTIFICATION_RATE_LIMIT)));
        }
        if (VerifyUtil.isEmpty(name) || VerifyUtil.isEmpty(idCardNo)
                || VerifyUtil.isEmpty(bankCardNo) || VerifyUtil.isEmpty(mobile)) {
            throw new Card4EmptyException(StringUtils.getStrParam("认证失败，请重新尝试，剩余认证次数%s次（%s/%s)",
                    String.valueOf(leftTimes),
                    String.valueOf(Constants.CERTIFICATION_RATE_LIMIT - leftTimes),
                    String.valueOf(Constants.CERTIFICATION_RATE_LIMIT)));
        }
        // TODO: if the mobile login not equal the c4 mobile

        Map<String, String> headers = new LinkedHashMap<>();
        headers.put("Authorization", Constants.QA_AUTHORIZATION_TOKEN);
        headers.put("Content-Type", "application/json");
        Map<String, String> params = new LinkedHashMap<>();
        params.put("mobile", mobile);
        params.put("id_card_no", idCardNo);
        params.put("name", name);
        params.put("bank_card_no", bankCardNo);
        String resp = HttpUtil.sendPost("http", 80, Constants.CERTIFICATION_HOST,
                Constants.CERTIFICATION_METHOD, headers, params);
        if (resp == null) {
            throw new CertificationInvalidException(StringUtils.getStrParam("认证失败，请重新尝试，剩余认证次数%s次（%s/%s)",
                    String.valueOf(leftTimes),
                    String.valueOf(Constants.CERTIFICATION_RATE_LIMIT - leftTimes),
                    String.valueOf(Constants.CERTIFICATION_RATE_LIMIT)));
        }
        logger.info("After c4, {resp: {}}", resp);

        JSONObject jsonRe = JSONObject.parseObject(resp);
        Customer acu = getByMobile(mobile);
        if (acu == null) {
            throw new CustomerNotFoundException();
        }
        try {
            String message = JSONObject.parseObject(jsonRe.getString("certification")).getString("message");
            if (!VerifyUtil.isEmpty(message) && message.contains(MagicConst.SAME_CERT)) {
                acu.setStatus(Constants.CUSTOMER_STATUS_BOUND);
                acu.setIdCardNo(idCardNo);
                acu.setName(name);
                acu.setBankCardNo(bankCardNo);
                acu.setUpdateTime(DateUtil.getNowTimestamp());
                update(acu);
            }
        } catch (Exception e) {
            throw new InvalidResponseException(StringUtils.getStrParam("认证失败，请重新尝试，剩余认证次数%s次（%s/%s)",
                    String.valueOf(leftTimes),
                    String.valueOf(Constants.CERTIFICATION_RATE_LIMIT - leftTimes),
                    String.valueOf(Constants.CERTIFICATION_RATE_LIMIT)));
        }
        return new AuthorizationTokenResponse(acu, authorizationToken);
    }

    private int checkCertificationRate(String mobile) {
        String rateStr = JedisUtil.get(Constants.BUSINESS_REDIS_URL,
                StringUtils.getStrParam(Constants.CERTIFICATION_RATE_KEY, mobile));
        if (VerifyUtil.isEmpty(rateStr)) {
            rateStr = "0";
        }
        int rate = Integer.parseInt(rateStr);
        if (rate >= Constants.CERTIFICATION_RATE_LIMIT) {
            return -1;
        }
        rate += 1;
        long nowTs = System.currentTimeMillis();
        Calendar c = Calendar.getInstance();
        int day = c.get(Calendar.DATE);
        c.set(Calendar.DATE, day + 1);
        c.set(Calendar.HOUR, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        long expireTs = c.getTimeInMillis();
        int expirePeriod = (int) (expireTs - nowTs);
        JedisUtil.set(Constants.BUSINESS_REDIS_URL,
                StringUtils.getStrParam(Constants.CERTIFICATION_RATE_KEY, mobile),
                String.valueOf(rate),
                expirePeriod / 1000);
        return Constants.CERTIFICATION_RATE_LIMIT - rate;
    }

    @Override
    public AuthorizationTokenResponse login(LoginRequest request, String accessToken) {
        if (request == null) {
            throw new EmptyRequestException();
        }
        String mobile = request.getMobile();
        String password = request.getPassword();
        if (VerifyUtil.isEmpty(mobile) || VerifyUtil.isEmpty(password)) {
            throw new MobilePasswordRequiredException();
        }
        String terminalId = JedisUtil.get(Constants.BUSINESS_REDIS_URL,
                StringUtils.getStrParam(Constants.ACCESS_TOKEN_TERMINAL_KEY, accessToken));
        if (!VerifyUtil.isEmpty(terminalId)) {
            Terminal ter = terminalService.getByUniqueNoFirst(terminalId, -1);
            if (ter != null) {
                ter.setStatus(Constants.LOGIN_YES);
                ter.setUpdateTime(new Timestamp(System.currentTimeMillis()));
                terminalService.update(ter);
            }
        }
        if (!RegexUtil.isSimplePass(password)) {
            throw new ParameterSimplePwdErrorException();
        }
        Customer cus = getByMobile(mobile);
        if (cus == null) {
            throw new CustomerNotFoundException();
        }
        if (cus.getStatus() == Constants.CUSTOMER_STATUS_TEMPORARY) {
            throw new CustomerNotExistsException();
        }
//        if (!VerifyUtil.bCryptCheck(password, cus.getPasswordDigest())) {
//            throw new AccountAuthFailureException();
//        }
        if(!HttpUtil.sendPut(password, cus.getPasswordDigest())){
            throw new AccountAuthFailureException();
        }
        int expiredPeriod = 0;
        if (cus.getStatus() == Constants.CUSTOMER_STATUS_BOUND) {
            expiredPeriod = Constants.REGISTER_TOKEN_EXPIRATION;
        }
        String authorizationToken = JedisUtil.get(Constants.BUSINESS_REDIS_URL,
                StringUtils.getStrParam(Constants.MOBILE_AUTHORIZATION_TOKEN_KEY, mobile));
        if (VerifyUtil.isEmpty(authorizationToken)) {
            authorizationToken = StringUtils.encoderByMd5(String.valueOf(System.currentTimeMillis() * 1000));
        }

        JedisUtil.set(Constants.BUSINESS_REDIS_URL,
                StringUtils.getStrParam(Constants.MOBILE_AUTHORIZATION_TOKEN_KEY, mobile),
                authorizationToken,
                expiredPeriod);
        JedisUtil.set(Constants.BUSINESS_REDIS_URL,
                StringUtils.getStrParam(Constants.AUTHORIZATION_TOKEN_KEY, authorizationToken),
                mobile,
                expiredPeriod);
        return new AuthorizationTokenResponse(cus, authorizationToken);
    }

    @Override
    public LoanResponseNextStr doCustomerLoans(CustomerLoansRequest req, String authorizationToken) {
        int pageLength = req.getLength();
        String nextIdStr = req.getNextId();

        String mobile = JedisUtil.get(Constants.BUSINESS_REDIS_URL,
                StringUtils.getStrParam(Constants.AUTHORIZATION_TOKEN_KEY, authorizationToken));
        if(VerifyUtil.isEmpty(mobile)){
            throw new AuthorizationTokenInvalidException();
        }

        long cusId = commonService.getCustomerIdByMobile(mobile);
        if(cusId < 0){
            throw new CustomerNotFoundException();
        }
        if(pageLength == 0){
            pageLength = Constants.PAGE_LENGTH_DEFAULT;
        }
        String nextId = DateUtil.getFormatDateTime();
        if(!VerifyUtil.isEmpty(nextIdStr) && !"0".equals(nextIdStr)){
            nextId = nextIdStr;
        }
        Map<String, Object> reMap = commonService.getHistories(
                Constants.CREDIT_TYPES_LOANS, cusId, pageLength, nextId);
        String nextStr = String.valueOf(reMap.get("next_id"));

        return new LoanResponseNextStr(Constants.RETURN_TYPE_LOAN,
                reMap.get("res_list"),
                VerifyUtil.isEmpty(nextStr)||"null".equals(nextStr)?null:nextStr);
    }
}
